package cn.donghuapian.component;

import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.instrument.classloading.LoadTimeWeaver;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Map;


/**
 * @description:
 * @author: hzw
 * @time: 2024/9/14 15:34
 */
@Component
public class SpringBeanUtils implements ApplicationContextAware, LoadTimeWeaverAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        applicationContext = context;
    }

    public static String getActiveProfile() {
        return applicationContext.getEnvironment().getActiveProfiles()[0];
    }

    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    public static <T> T getBean(String name, Class<T> clz) {
        return applicationContext.getBean(name, clz);
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

    public static <T> Map<String, T> getBeanMap(Class<T> clazz) {
        return applicationContext.getBeansOfType(clazz);
    }

    public static Object getTarget(Object proxy) throws Exception {
        if (!AopUtils.isAopProxy(proxy)) {
            // 不是代理对象,直接返回
            return proxy;
        }

        if (AopUtils.isJdkDynamicProxy(proxy)) {
            return getJdkDynamicProxyTargetObject(proxy);
        } else {
            // cglib
            return getCglibProxyTargetObject(proxy);
        }

    }

    public static Object replaceBean(String beanName, Class clazz) throws InstantiationException, IllegalAccessException {
        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) ((AbstractApplicationContext) applicationContext).getBeanFactory();

        if (factory.containsBean(beanName)) {
            factory.removeBeanDefinition(beanName);
            factory.registerBeanDefinition(beanName, BeanDefinitionBuilder.genericBeanDefinition(clazz).getBeanDefinition());
            factory.autowireBean(clazz.newInstance());
        }
        return applicationContext.getBean(beanName);
    }

    public static void destroyBean(String... beanNames) {
        if (beanNames == null) {
            return;
        }
        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) ((AbstractApplicationContext) applicationContext).getBeanFactory();
        for (String beanName : beanNames) {
            if (factory.isSingleton(beanName)) {
                factory.destroySingleton(beanName);
            }
        }
    }

    private static Object getJdkDynamicProxyTargetObject (Object proxy)throws Exception {
        Field field = proxy.getClass().getSuperclass().getDeclaredField("h");
        field.setAccessible(true);
        AopProxy aopProxy = (AopProxy) field.get(proxy);
        Field advised = aopProxy.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
        return target;
    }

    private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
        Field field = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        field.setAccessible(true);
        Object dynamicAdvisedInterceptor = field.get(proxy);
        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
        return target;
    }

    @Override
    public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
        return;
    }

}